package cn.kmsoft.gistool.gpkg;

import java.util.ArrayList;
import java.util.List;

import org.geotools.geometry.jts.JTSFactoryFinder;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;

import mil.nga.sf.LineString;
import mil.nga.sf.MultiLineString;
import mil.nga.sf.MultiPoint;
import mil.nga.sf.MultiPolygon;
import mil.nga.sf.Point;
import mil.nga.sf.Polygon;

/**
 * mil.nga.sf NF框架类库下WKT数据操作类，主要是完成WKT/WKB的面和多面的图形构建
 * 
 * @author cqwensten 2020-08-05
 *
 */
public class NFGeometryWKTUtil {

	/**
	 * create a rectangle(矩形)
	 * 
	 * @param x1 the first x-value
	 * @param x2 the second x-value
	 * @param y1 the first y-value
	 * @param y2 the second y-value
	 * @return
	 */
	public static Envelope createEnvelope(double x1, double x2, double y1, double y2) {
		Envelope envelope = new Envelope(x1, x2, y1, y2);
		return envelope;
	}

	/**
	 * create a point by WKT
	 * 
	 * @return
	 * @throws ParseException
	 */
	public static Point createPointByWKT(String wkt) throws ParseException {
		GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);
		WKTReader reader = new WKTReader(geometryFactory);

		// Point point = (Point) reader.read("POINT (109.013388 32.715519)");
		com.vividsolutions.jts.geom.Point jtsPoint = (com.vividsolutions.jts.geom.Point) reader.read(wkt);
		Point point = new Point(jtsPoint.getX(), jtsPoint.getY());

		return point;
	}

	/**
	 * create mil.nga.sf.MultiPoint by wkt
	 * 
	 * @return
	 */
	public static MultiPoint createMulPointByWKT(String wkt) throws ParseException {
		GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);
		WKTReader reader = new WKTReader(geometryFactory);
		// MultiPoint mpoint = (MultiPoint) reader.read("MULTIPOINT(109.013388
		// 32.715519,119.32488 31.435678)");
		com.vividsolutions.jts.geom.MultiPoint jtsMpoint = (com.vividsolutions.jts.geom.MultiPoint) reader.read(wkt);
		List<Point> points = new ArrayList<Point>();
		Coordinate[] coords = jtsMpoint.getCoordinates();
		for (int i = 0; i < coords.length; i++) {
			Coordinate coord = coords[i];
			points.add(new Point(coord.x, coord.y));
		}

		MultiPoint mpoint = new MultiPoint();
		mpoint.setPoints(points);
		return mpoint;
	}

	/**
	 * create a mil.nga.sf.LineString by WKT
	 * 
	 * @return
	 * @throws ParseException
	 */
	public static LineString createLineByWKT(String wkt) throws ParseException {
		GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);
		WKTReader reader = new WKTReader(geometryFactory);

		// LineString line = (LineString) reader.read("LINESTRING(0 0, 2 0)");
		com.vividsolutions.jts.geom.LineString jtsLine = (com.vividsolutions.jts.geom.LineString) reader.read(wkt);
		List<Point> points = new ArrayList<Point>();
		Coordinate[] coords = jtsLine.getCoordinates();
		for (int i = 0; i < coords.length; i++) {
			Coordinate coord = coords[i];
			points.add(new Point(coord.x, coord.y));
		}

		LineString line = new LineString();
		line.addPoints(points);

		return line;
	}

	/**
	 * create mil.nga.sf.MultiLineString by WKT
	 * 
	 * @return
	 * @throws ParseException
	 */
	public static MultiLineString createMLineByWKT(String wkt) throws ParseException {
		GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);
		WKTReader reader = new WKTReader(geometryFactory);

		// MultiLineString line = (MultiLineString) reader.read("MULTILINESTRING((0 0, 2
		// 0),(1 1,2 2))");
		com.vividsolutions.jts.geom.MultiLineString jtsMLine = (com.vividsolutions.jts.geom.MultiLineString) reader
				.read(wkt);
		int num = jtsMLine.getNumGeometries();
		List<LineString> lineStrings = new ArrayList<LineString>();
		for (int i = 0; i < num; i++) {
			com.vividsolutions.jts.geom.LineString jtsLine = (com.vividsolutions.jts.geom.LineString) jtsMLine
					.getGeometryN(i);
			Coordinate[] coords = jtsLine.getCoordinates();

			List<Point> points = new ArrayList<Point>();
			for (int j = 0; j < coords.length; j++) {
				Coordinate coord = coords[i];
				points.add(new Point(coord.x, coord.y));
			}
			LineString line = new LineString();
			line.addPoints(points);

			lineStrings.add(line);
		}
		MultiLineString mLine = new MultiLineString();
		mLine.addLineStrings(lineStrings);

		return mLine;
	}

	/**
	 * create a mil.nga.sf.Polygon(多边形) by WKT
	 * 
	 * @return
	 * @throws ParseException
	 */
	public static Polygon createPolygonByWKT(String wkt) throws ParseException {
		GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);
		WKTReader reader = new WKTReader(geometryFactory);
		// Polygon polygon = (Polygon) reader.read("POLYGON((20 10, 30 0, 40 10, 30 20,
		// 20 10))");
		com.vividsolutions.jts.geom.Polygon jtsPolygon = (com.vividsolutions.jts.geom.Polygon) reader.read(wkt);
		List<LineString> lineStrings = new ArrayList<LineString>();
		// 1-外环
		com.vividsolutions.jts.geom.LineString jtsExterRing = jtsPolygon.getExteriorRing();
		LineString exterRing = new LineString();
		List<Point> extRingPoints = new ArrayList<Point>();
		Coordinate[] extRingCoords = jtsExterRing.getCoordinates();
		int numOfERingPoint = extRingCoords.length;
		for (int i = 0; i < numOfERingPoint; i++) {
			Coordinate coord = extRingCoords[i];
			extRingPoints.add(new Point(coord.x, coord.y));
		}
		exterRing.setPoints(extRingPoints);
		lineStrings.add(0, exterRing);

		// 2-内环-岛宗
		int numOfInerRing = jtsPolygon.getNumInteriorRing();
		for (int n = 0; n < numOfInerRing; n++) {
			com.vividsolutions.jts.geom.LineString jtsInRing = jtsPolygon.getInteriorRingN(n);
			Coordinate[] inRingCoords = jtsInRing.getCoordinates();

			LineString inRing = new LineString();
			List<Point> inRingPoints = new ArrayList<Point>();
			for (int i = 0; i < inRingCoords.length; i++) {
				Coordinate coord = inRingCoords[i];
				inRingPoints.add(new Point(coord.x, coord.y));
			}
			inRing.addPoints(inRingPoints);

			lineStrings.add(n + 1, inRing);
		}

		Polygon polygon = new Polygon();
		polygon.addRings(lineStrings);

		return polygon;
	}

	/**
	 * com.vividsolutions.jts.Polygon  转换为  mil.nga.sf.Polygon
	 * 
	 * @param jtsPolygon
	 * @return
	 * @throws ParseException
	 */
	public static Polygon jtsPolygon2NFPolygon(com.vividsolutions.jts.geom.Polygon jtsPolygon) throws ParseException {
		List<LineString> lineStrings = new ArrayList<LineString>();
		// 1-外环
		com.vividsolutions.jts.geom.LineString jtsExterRing = jtsPolygon.getExteriorRing();
		LineString exterRing = new LineString();
		List<Point> extRingPoints = new ArrayList<Point>();
		Coordinate[] extRingCoords = jtsExterRing.getCoordinates();
		int numOfERingPoint = extRingCoords.length;
		for (int i = 0; i < numOfERingPoint; i++) {
			Coordinate coord = extRingCoords[i];
			extRingPoints.add(new Point(coord.x, coord.y));
		}
		exterRing.setPoints(extRingPoints);
		lineStrings.add(0, exterRing);

		// 2-内环-岛宗
		int numOfInerRing = jtsPolygon.getNumInteriorRing();
		for (int n = 0; n < numOfInerRing; n++) {
			com.vividsolutions.jts.geom.LineString jtsInRing = jtsPolygon.getInteriorRingN(n);
			Coordinate[] inRingCoords = jtsInRing.getCoordinates();

			LineString inRing = new LineString();
			List<Point> inRingPoints = new ArrayList<Point>();
			for (int i = 0; i < inRingCoords.length; i++) {
				Coordinate coord = inRingCoords[i];
				inRingPoints.add(new Point(coord.x, coord.y));
			}
			inRing.addPoints(inRingPoints);

			lineStrings.add(n + 1, inRing);
		}

		Polygon polygon = new Polygon();
		polygon.addRings(lineStrings);

		return polygon;
	}

	/**
	 * create mil.nga.sf.MultiPolygon by wkt
	 * 
	 * @param wkt  例如：wkt="MULTIPOLYGON(((40 10, 30 0, 40 10, 30 20, 40 10),(30 10, 30 0, 40 10, 30 20, 30 10)))"
	 * @return
	 * @throws ParseException
	 */
	public static MultiPolygon createMulPolygonByWKT(String wkt) throws ParseException {
		GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);
		WKTReader reader = new WKTReader(geometryFactory);

		// MultiPolygon mpolygon = (MultiPolygon) reader.read("MULTIPOLYGON(((40 10, 30
		// 0, 40 10, 30 20, 40 10),(30 10, 30 0, 40 10, 30 20, 30 10)))");
		MultiPolygon mpolygon = new MultiPolygon();

		com.vividsolutions.jts.geom.MultiPolygon jtsMPolygon = (com.vividsolutions.jts.geom.MultiPolygon) reader
				.read(wkt);
		int numOfPolygon = jtsMPolygon.getNumGeometries();
		for (int i = 0; i < numOfPolygon; i++) {
			com.vividsolutions.jts.geom.Polygon jtsPolygon = (com.vividsolutions.jts.geom.Polygon) jtsMPolygon
					.getGeometryN(i);
			Polygon py = jtsPolygon2NFPolygon(jtsPolygon);
			mpolygon.addGeometry(py);
		}

		return mpolygon;
	}
	
	//wkt
	private static String getPointText(Point point) {
		return "(" + point.getX() + " " + point.getY() + ")";
	}
	private static String getMultiPointText(MultiPoint multiPoint) {
		Point point=null;
		int num = multiPoint.numGeometries();
		StringBuffer sb = new StringBuffer("(");
		for (int i = 0; i < num; i++) {
			point = (Point) multiPoint.getGeometry(i);
			sb.append(getPointText(point) + ",");
		}
		sb.setCharAt(sb.length() - 1, ')');
		return sb.toString();
	}
	/**
	 * 获取LineString的Text
	 * 
	 * @param lineString  mil.nga.sf.LineString;
	 * @return
	 */
	private static String getLineStringText(LineString lineString) {
		List<Point> corrds = lineString.getPoints();
		StringBuffer sb = new StringBuffer("(");
		for (Point corrd : corrds) {
			sb.append( corrd.getX()+ " "+ corrd.getY()+",");
		}
		sb.setCharAt(sb.length() - 1, ')');
		return sb.toString();
	}
	/**
	 * 获取LineString的Text，注意该Text是首尾重叠的
	 * 
	 * @param lineString  mil.nga.sf.LineString;
	 * @return
	 */
	private static String getLineStringTextWithCloseRing(LineString lineString) {
		List<Point> coords = lineString.getPoints();
		StringBuffer sb = new StringBuffer("(");
		Point firstCorrd = null;
		Point lastCoord = null;
		int pointSize = coords.size();
		for (int i=0; i<pointSize; i++) {
			Point coord = coords.get(i);			
			if(i==0) {
				firstCorrd = coord;
			}
			if(i==(pointSize-1)) {
				lastCoord = coord;
			}
			sb.append( coord.getX()+ " "+ coord.getY()+",");
		}
		//要求末界址点必须和第一个界址点重叠
		if( !firstCorrd.equals(lastCoord)) {
			sb.append( firstCorrd.getX()+ " "+ firstCorrd.getY());
		}else {
			sb.deleteCharAt(sb.length() - 1 );
		}
		
		sb.append(')');
		return sb.toString();
	}
	private static String getMultiLineStringText(MultiLineString multiLineString) {
		LineString lineString;
		int num = multiLineString.numGeometries();
		StringBuffer sb = new StringBuffer("(");
		for (int i = 0; i < num; i++) {
			lineString = (LineString) multiLineString.getLineString(i);
			sb.append(getLineStringText(lineString) + ",");
		}
		sb.setCharAt(sb.length() - 1, ')');
		return sb.toString();
	}
	
	private static String getPolygonText(Polygon polygon) {
		String result = null;
		int num = polygon.numInteriorRings();
		if (num > 0) {
			//外环
			StringBuffer sb = new StringBuffer("(" + getLineStringTextWithCloseRing(polygon.getExteriorRing()) + ",");
			//内环-岛
			for (int i = 0; i < num; i++) {
				sb.append(getLineStringTextWithCloseRing(polygon.getInteriorRing(i)) + ",");
			}
			sb.setCharAt(sb.length() - 1, ')');
			result = sb.toString();
		} 
		else {
			//只有外环
			result = "(" + getLineStringTextWithCloseRing(polygon.getExteriorRing()) + ")";
		}
		return result;
	}
	private static String getMultiPolygonText(MultiPolygon multiPolygon) {
		Polygon polygon;
		int num = multiPolygon.numGeometries();
		StringBuffer sb = new StringBuffer("(");
		for (int i = 0; i < num; i++) {
			polygon = (Polygon) multiPolygon.getGeometry(i);
			sb.append(getPolygonText(polygon) + ",");
		}
		sb.setCharAt(sb.length() - 1, ')');
		return sb.toString();
	}
	
	/**
	 * nfGeom2Wkt 获取Geometry的Wkt
	 * 
	 * @param geometry mil.nga.sf.Geometry的图形
	 * @return  wkt描述的图形分为POINT、MULTIPOINT、LINESTRING、MULTILINESTRING、POLYGON、MULTIPOLYGON
	 */
	public static String nfGeom2Wkt(mil.nga.sf.Geometry geometry) {
		// wkt描述的图形分为POINT、MULTIPOINT、LINESTRING、MULTILINESTRING、POLYGON、MULTIPOLYGON这几种类型。
		String wkt = null;
		
		// MultiPolygon
		if (geometry instanceof mil.nga.sf.MultiPolygon) {
			mil.nga.sf.MultiPolygon mpy = (mil.nga.sf.MultiPolygon) geometry;
			String geomText = getMultiPolygonText(mpy);
			wkt = "MULTIPOLYGON"+geomText;
		}
		else if (geometry instanceof mil.nga.sf.Polygon) {
			mil.nga.sf.Polygon py = (mil.nga.sf.Polygon) geometry;
			String geomText = getPolygonText(py);
			wkt = "POLYGON"+geomText;
		}
		else if(geometry instanceof mil.nga.sf.MultiLineString) {
			mil.nga.sf.MultiLineString mpl = (mil.nga.sf.MultiLineString) geometry;
			String geomText = getMultiLineStringText(mpl);
			wkt = "MULTILINESTRING"+geomText;
		}
		else if(geometry instanceof mil.nga.sf.LineString) {
			mil.nga.sf.LineString pl = (mil.nga.sf.LineString) geometry;
			String geomText = getLineStringText(pl);
			wkt = "LINESTRING"+geomText;
		}
		else if(geometry instanceof mil.nga.sf.MultiPoint) {
			mil.nga.sf.MultiPoint mpt = (mil.nga.sf.MultiPoint) geometry;
			String geomText = getMultiPointText(mpt);
			wkt = "MULTIPOINT"+geomText;
		}
		else if(geometry instanceof mil.nga.sf.Point) {
			mil.nga.sf.Point pt = (mil.nga.sf.Point) geometry;
			String geomText = getPointText(pt);
			wkt = "POINT"+geomText;
		}
		
		return wkt;
	}
	

	/**
	 * wangq 代码测试
	 * 
	 * @param args
	 * @throws ParseException
	 */
	public static void main(String[] args) throws ParseException {

		String wkt = "MULTIPOLYGON(((40 10, 30 0, 40 10, 30 20, 40 10),(30 10, 30 0, 40 10, 30 20, 30 10)))";

		MultiPolygon mp = NFGeometryWKTUtil.createMulPolygonByWKT(wkt);
		//
		List<Polygon> polygons = mp.getPolygons();
		int numOfPolygon = polygons.size();
		for (int i = 0; i < numOfPolygon; i++) {
			Polygon p = polygons.get(i);

			List<LineString> rings = p.getRings();
			for (int j = 0; j < rings.size(); j++) {
				LineString ring = rings.get(j);
				List<Point> pts = ring.getPoints();
				for (int n = 0; n < pts.size(); n++) {
					Point coord = pts.get(n);
					System.out.println(String.format("%d---%d---%d, %.3f, %.3f", i, j, n, coord.getX(), coord.getY()));
				}
			}
		}
	}
}
